package com.lhs.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.feign.user.UserFeignClient;
import com.lhs.DTO.blog.BlogDTO;
import com.lhs.Result.ResultCode;
import com.lhs.config.redis.ZiDeRedisUtil;
import com.lhs.entity.blog.article_search;
import com.lhs.entity.blog.blog_cover;
import com.lhs.entity.blog.blog_label;
import com.lhs.mapper.blog_coverMapper;
import com.lhs.mapper.blog_labelMapper;
import com.lhs.mapper.blog_post_0Mapper;
import com.lhs.Page.PageParam;
import com.lhs.Page.PageResult;
import com.lhs.Result.Result;
import com.lhs.service.blog_post_0Service;
import com.lhs.VO.blog.BlogVO;
import com.lhs.VO.user.UserInfoVO;
import com.lhs.entity.blog.blog_post;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
* @author 15977
* @description 针对表【blog_post_0(博客文章表（分表）)】的数据库操作Service实现
* @createDate 2025-09-06 01:49:57
*/
@Slf4j
@Service
public class blog_post_0ServiceImpl extends ServiceImpl<blog_post_0Mapper, blog_post>
    implements blog_post_0Service {

    // 缓存池 key
    private static final String BLOG_ID_POOL_KEY = "blog:post:id:pool";
    private static final String BLOG_ID_POOL_LOCK = "lock:blog:post:id:pool";
    private static final String BLOG_ID_POOL_EMPTY = "blog:post:id:pool:empty";

    // 基础过期（秒）
    private static final long BASE_EXPIRE_SECONDS = 3600; // 1小时
    private static final long JITTER_SECONDS = 600; // 10分钟抖动

    @Autowired
    private ZiDeRedisUtil redisUtil;


    @Autowired
    private RedissonClient redissonClient;

    @Autowired
    private blog_post_0Mapper blog_post_0Mapper;

    @Autowired
    private blog_coverMapper blogCoverMapper;

    @Autowired
    private blog_labelMapper blogLabelMapper;

    @Autowired
    private blog_coverServiceImpl blog_coverServiceImpl;

    @Autowired
    private UserFeignClient userFeignClient;



    // 获取所有博客
    @Override
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public Result<PageResult<BlogVO>> getAllList(PageParam pageParam) {
        // 构建分页参数
        Page<blog_post> page = pageParam.buildPage();

        // 执行分页查询
        IPage<blog_post> blogPage = blog_post_0Mapper.selectAllBlog(page);

        // 转换成 VO
        List<BlogVO> voList = blogPage.getRecords().stream().map(blog_post -> {
            BlogVO blogVO = new BlogVO();
            blogVO.setId(blog_post.getId());
            blogVO.setTitle(blog_post.getTitle());
            blogVO.setContent(blog_post.getContent());
            blogVO.setLikeCount(blog_post.getLikeCount());
            blogVO.setCommentCount(blog_post.getCommentCount());
            blogVO.setStatus(blog_post.getStatus());
            blogVO.setAuthorId(blog_post.getUserId());
            if (blog_post.getPublishTime() != null){
                blogVO.setPublishTime(blog_post.getPublishTime());
            }else {
                blogVO.setPublishTime(blog_post.getCreateTime());
            }

            blogVO.setUpdateTime(blog_post.getUpdateTime());

            // 文章封面
            blogVO.setCover(blog_coverServiceImpl.getBlogCover(blog_post.getId()).getData().getCover());

            try {
                Result<UserInfoVO> userInfoVO = userFeignClient.getUserInfoById(blog_post.getUserId());
                if (userInfoVO != null && userInfoVO.getData() != null) {
                    blogVO.setAuthorName(userInfoVO.getData().getNickname());
                    blogVO.setAuthorAvatar(userInfoVO.getData().getAvatar());
                }
            } catch (Exception e) {
                log.error("获取用户信息失败，userId:"+blog_post.getUserId()+ e);
            }
            return blogVO;
        }).toList();

        // 构造分页结果
        PageResult<BlogVO> pageResult = new PageResult<>(
                blogPage.getCurrent(),
                blogPage.getSize(),
                blogPage.getPages(),
                blogPage.getTotal(),
                voList
        );

        return Result.success(pageResult);
    }






    /**
     * 全量刷新缓存池（带 Redisson 分布式锁，防击穿）
     */
    public void initBlogIdPool() {
        RLock lock = redissonClient.getLock(BLOG_ID_POOL_LOCK);
        boolean locked = false;
        try {
            // 尝试获取锁，等待最多 2 秒，锁持有 30 秒自动释放（租约）
            locked = lock.tryLock(2, 30, TimeUnit.SECONDS);
            if (!locked) {
                log.info("有其他节点正在刷新缓存池，跳过本次刷新");
                return;
            }

            log.info("开始全量刷新博客 ID 池（持锁）...");
            List<Long> ids = blog_post_0Mapper.selectAllIds();

            if (ids == null || ids.isEmpty()) {
                // 写空占位以防穿透
                redisUtil.setCacheObject(BLOG_ID_POOL_EMPTY, "EMPTY", 60, TimeUnit.SECONDS);
                redisUtil.deleteObject(BLOG_ID_POOL_KEY); // 删除旧集
                log.warn("DB 无可用文章，写入空占位并删除旧集合");
                return;
            }

            // 覆盖式更新集合（如数据量大，可按批次插入）
            redisUtil.deleteObject(BLOG_ID_POOL_KEY);
            Set<String> idStrSet = ids.stream().map(String::valueOf).collect(Collectors.toSet());
            // 使用新增的 set 写入方法
            redisUtil.addToSet(BLOG_ID_POOL_KEY, idStrSet.toArray(new String[0]));

            // 随机 TTL 防雪崩
            long expire = BASE_EXPIRE_SECONDS + ThreadLocalRandom.current().nextLong(0, JITTER_SECONDS);
            redisUtil.expire(BLOG_ID_POOL_KEY, expire, TimeUnit.SECONDS);

            log.info("刷新完成，大小：{}，expire={}s", redisUtil.getSetSize(BLOG_ID_POOL_KEY), expire);

        } catch (InterruptedException ie) {
            Thread.currentThread().interrupt();
            log.error("刷新缓存池时被中断", ie);
        } catch (Exception e) {
            log.error("刷新缓存池异常", e);
        } finally {
            if (locked) {
                try {
                    lock.unlock();
                } catch (Exception e) {
                    log.warn("释放 Redisson 锁失败", e);
                }
            }
        }
    }

    /**
     * 获取随机 N 个 ID（主入口）
     */
    public List<Long> getRandomIds(int limit) {
        // 1. 若存在空占位，直接返回空，防止穿透
        if (Boolean.TRUE.equals(redisUtil.getCacheObject(BLOG_ID_POOL_EMPTY))) {
            log.warn("检测到空占位，直接返回空列表（防穿透）");
            return Collections.emptyList();
        }

        Long poolSize = redisUtil.getSetSize(BLOG_ID_POOL_KEY);
        if (poolSize == null || poolSize == 0) {
            // 池为空 —— 尝试获取锁并同步（避免击穿）
            RLock lock = redissonClient.getLock(BLOG_ID_POOL_LOCK);
            boolean locked = false;
            try {
                locked = lock.tryLock(0, 30, TimeUnit.SECONDS);
                if (locked) {
                    // 双重检查
                    Long sizeAfter = redisUtil.getSetSize(BLOG_ID_POOL_KEY);
                    if (sizeAfter == null || sizeAfter == 0) {
                        initBlogIdPool(); // 同步刷新
                    }
                } else {
                    // 未获得锁：另一个节点在刷新，短路返回空，避免同时回 DB（可改为返回 DB 回退，但要小心并发）
                    log.warn("未获得锁且池为空，短路返回空，避免击穿 DB");
                    return Collections.emptyList();
                }
            } catch (InterruptedException ie) {
                Thread.currentThread().interrupt();
                log.error("尝试获取刷新锁被中断", ie);
                return Collections.emptyList();
            } finally {
                if (locked) {
                    try { lock.unlock(); } catch (Exception ignore) {}
                }
            }
        } else if (poolSize < Math.max(1, limit * 5)) {
            // 池子较小，触发异步补充，但不阻塞
            asyncRefresh();
        }

        // 从集合随机取成员（SRANDMEMBER）
        Set<Object> randomIdSet = redisUtil.randomMembersFromSet(BLOG_ID_POOL_KEY, limit);
        List<String> randomIds = randomIdSet.stream()
                .filter(Objects::nonNull)
                .map(Object::toString)
                .collect(Collectors.toList());


        // 返回 Long 列表（去重并保序）
        return randomIds.stream()
                .filter(Objects::nonNull)
                .map(Long::valueOf)
                .distinct()
                .collect(Collectors.toList());
    }
@Override
@Transactional
public Result addBlog(BlogDTO blog) {
    if (blog == null) {
        return Result.error("博客参数不能为空");
    }

    // 验证必要参数
    if (blog.getTitle() == null || blog.getContent() == null) {
        return Result.error("博客标题和内容不能为空");
    }

    try {
        blog_post blogPost = new blog_post();
        blogPost.setTitle(blog.getTitle());
        blogPost.setContent(blog.getContent());
        blogPost.setCreateTime(blog.getCreateTime());
        blogPost.setUserId(StpUtil.getLoginIdAsLong());
        blogPost.setPublishTime(blog.getPublishTime());
        blogPost.setStatus(blog.getStatus());
        blog_post_0Mapper.saveBlog(blogPost);

        // 检查博客是否保存成功
        if (blogPost.getId() == null) {
            throw new RuntimeException("文章保存失败，无法获取博客ID");
        }

        // 保存博客封面
        if (blog.getCover() != null) {
            blog_cover blogCover = new blog_cover();
            blogCover.setPostId(blogPost.getId());
            blogCover.setCover(blog.getCover());
            blogCoverMapper.saveCover(blogCover);
        }

        // 保存博客标签
        if (blog.getLabel() != null) {
            blog_label blogLabel = new blog_label();
            blogLabel.setPostId(blogPost.getId());
            blogLabel.setLabel(blog.getLabel());
            blogLabelMapper.saveLabel(blogLabel);
        }

        // 添加文章ID到缓存池
        addBlogId(blogPost.getId());

        return Result.success( "添加文章成功");
    } catch (Exception e) {
        // 记录异常日志
        throw new RuntimeException("添加文章失败", e);
    }
}


 @Override
@Transactional
public Result removeById(Long id) {
    // 参数校验
    if (id == null || id <= 0) {
        return Result.error("参数错误");
    }

    try {
        // 获取当前登录用户ID
        long currentUserId = StpUtil.getLoginIdAsLong();

        // 查询博客信息
        blog_post blog = blog_post_0Mapper.getBlogPostById(id);
        if (blog == null) {
            return Result.error("博客不存在");
        }

        // 权限校验：检查博客是否属于当前用户或当前用户是否为管理员
        boolean isOwner = blog.getUserId() == currentUserId;
        boolean isAdmin = false;
        try {
            StpUtil.checkRole("admin");
            isAdmin = true;
        } catch (Exception e) {
            // 忽略非管理员异常
        }

        if (!isOwner && !isAdmin) {
            log.warn("用户 {} 无权限删除博客 {}", currentUserId, id);
            return Result.error("无权限删除该博客");
        }

        // 执行删除操作
        removeBlogId(id);
        blog_post_0Mapper.removeById(id);
        blogCoverMapper.removeById(id);
        blogLabelMapper.removeById(id);

        return Result.success("删除成功");

    } catch (Exception e) {
        // 记录异常日志，事务会自动回滚
        log.error("删除博客失败，博客ID: {}, 错误信息: {}", id, e.getMessage(), e);
        throw e; // 重新抛出异常以触发事务回滚
    }
}


/**
     * 获取博客详情
     *
     * @param postId 博客ID
     * @return 博客详情
     */
@Override
public Result<BlogVO> getBlogPostById(Long postId) {
    // 参数校验
    if (postId == null || postId <= 0) {
        return Result.error("参数错误");
    }

    // 查询博客文章
    blog_post blogPost = blog_post_0Mapper.getBlogPostById(postId);
    if (blogPost == null) {
        return Result.error("文章不存在");
    }

    // 权限判断
    boolean isPublic = blogPost.getStatus() == 1;
    boolean isAuthor = false;
    boolean isAdmin = false;
    try {
        isAuthor = StpUtil.isLogin() && blogPost.getUserId().equals(StpUtil.getLoginIdAsLong());
    } catch (Exception e) {
        // 忽略权限判断异常，视为非作者
      log.warn("用户未登录，视为非作者访问");
    }try {
        StpUtil.checkRole("admin");
        isAdmin = true;
    } catch (Exception e) {
        // 忽略非管理员异常
    }

    if (!isPublic && !isAuthor && !isAdmin) {
        return Result.error("无访问权限");
    }

    // 构造 VO 对象
    BlogVO blogVO = new BlogVO();
    blogVO.setId(blogPost.getId());
    blogVO.setTitle(blogPost.getTitle());
    blogVO.setContent(blogPost.getContent());
    blogVO.setAuthorId(blogPost.getUserId());
    blogVO.setPublishTime(blogPost.getPublishTime());
    blogVO.setUpdateTime(blogPost.getUpdateTime());
    blogVO.setStatus(blogPost.getStatus());

    // 查询用户信息
    Result<UserInfoVO> userResult = userFeignClient.getUserInfoById(blogPost.getUserId());
    if (userResult != null && userResult.getCode() == ResultCode.SUCCESS.getCode() && userResult.getData() != null) {
        UserInfoVO user = userResult.getData();
        blogVO.setAuthorName(user.getNickname());
        blogVO.setAuthorAvatar(user.getAvatar());
    }

    // 设置封面和标签
    blogVO.setCover(blogCoverMapper.getCoverByPostId(postId));
    blogVO.setLabel(blogLabelMapper.getLabelByPostId(postId));

    return Result.success(blogVO);
}
    // 获取所有文章列表(仅公开)
    @Override
    public List<article_search> getAllListblogs() {
        List<article_search> blogPosts   = blog_post_0Mapper.getAllListblogs();

        List<article_search> voList = blogPosts.stream().map(blog_post -> {
            article_search blogVO = new article_search();
            blogVO.setId(blog_post.getId());
            blogVO.setTitle(blog_post.getTitle());
            blogVO.setContent(blog_post.getContent());
            blogVO.setLikeCount(blog_post.getLikeCount());
            blogVO.setCommentCount(blog_post.getCommentCount());
            blogVO.setStatus(blog_post.getStatus());
            blogVO.setViewCount(blog_post.getViewCount());
            blogVO.setUpdateTime(blog_post.getUpdateTime());
            blogVO.setPublishTime(blog_post.getPublishTime());

            blogVO.setAuthorId(blog_post.getAuthorId());
            if (blog_post.getPublishTime() == null){
                blog_post.setPublishTime(blog_post.getCreateTime());
            }
            blogVO.setCover(blog_post.getCover());

            // 文章封面


            try {
                Result<UserInfoVO> userInfoVO = userFeignClient.getUserInfoById(blog_post.getAuthorId());
                if (userInfoVO != null && userInfoVO.getData() != null) {
                    blogVO.setAuthorName(userInfoVO.getData().getNickname());
                    blogVO.setAuthorAvatar(userInfoVO.getData().getAvatar());
                }
            } catch (Exception e) {
                log.error("获取用户信息失败，userId:"+blog_post.getAuthorId()+ e);
            }
            return blogVO;
        }).toList();

        return voList;
    }



    /**
     * 新增文章时，单独插入缓存池（实时更新）
     */
    public void addBlogId(Long id) {
        redisUtil.addToSet(BLOG_ID_POOL_KEY, String.valueOf(id));
        log.info("新增 id {} 已加入池", id);
    }

    /**
     * 删除文章时，从缓存池移除
     */
    public void removeBlogId(Long id) {
        redisUtil.removeFromSet(BLOG_ID_POOL_KEY, String.valueOf(id));
        log.info("删除 id {} 已从池移除", id);
    }

    /**
     * 异步刷新（用于池子不足时）
     */
    @Async
    public void asyncRefresh() {
        try {
            log.info("触发异步刷新缓存池...");
            initBlogIdPool();
        } catch (Exception e) {
            log.error("异步刷新失败", e);
        }
    }


}




